package ma.myschool.app.webapp.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ma.myschool.app.Constants.GenreEleve;
import ma.myschool.app.beans.AttestationScolaireBean;
import ma.myschool.app.beans.EleveBean;
import ma.myschool.app.beans.LabelValue;
import ma.myschool.app.beans.RetardPay;
import ma.myschool.app.model.AnneeScolaire;
import ma.myschool.app.model.Classe;
import ma.myschool.app.model.Eleve;
import ma.myschool.app.model.Groupe;
import ma.myschool.app.model.MoisPaye;
import ma.myschool.app.model.Paiement;
import ma.myschool.app.service.AnneeScolaireManager;
import ma.myschool.app.service.ClasseManager;
import ma.myschool.app.service.EleveManager;
import ma.myschool.app.service.GroupeManager;
import ma.myschool.app.service.MoisPayeManager;
import ma.myschool.app.service.PaiementManager;
import ma.myschool.app.util.DateUtil;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRPdfExporterParameter;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.omnifaces.util.Faces;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("eleveController")
@Scope("session")
public class EleveController extends BasePage implements Serializable {
    
    private EleveManager eleveManager;
    private ClasseManager classeManager;
    private GroupeManager groupeManager;
    private AnneeScolaireManager anneeScolaireManager;
    private PaiementManager paiementManager;
    private MoisPayeManager moisPayeManager;
    
    private List<Eleve> elevesResult;
    private List<Eleve> importResult;
    private List<Eleve> retardPayEleves;
    private List<Classe> classes;
    private List<Groupe> groupes;
    private Eleve eleve = new Eleve();
    
    private UploadedFile fileEleves;
    
    private Long id;
    private String query;
    private Classe classe = new Classe();
    private MoisPaye moisPayeNow;
    private String classeurName;

    public void setQuery(String query) {
        this.query = query;
    }

    public EleveController() {
        setSortColumn("numeroInscription"); // sets the default sort column
    }

    public String search() {
    	elevesResult = eleveManager.searchCustom(eleve);
        return "eleveSearched";
    }
    
    public String delete() {
    	id = new Long(getParameter("id"));
    	eleveManager.deleteEleve(id);
    	
    	search();
    	
        addMessage("eleve.deleted");

        return "success";
    }

    public String edit() {
        // Workaround for not being able to set the id using #{param.id} when using Spring-configured managed-beans
        id = new Long(getParameter("id"));
        
        // Comparison to zero (vs. null) is required with MyFaces 1.2.2, not with previous versions
        if (id != null && id != 0) {
            eleve = eleveManager.get(id);
        } else {
            eleve = new Eleve();
        }

        return "edit";
    }
    
    public String viewEleveForm(){
    	eleve = new Eleve();
    	return "eleveForm";    	
    }

    public String detail() {
        // Workaround for not being able to set the id using #{param.id} when using Spring-configured managed-beans
        id = new Long(getParameter("id"));
        
        // Comparison to zero (vs. null) is required with MyFaces 1.2.2, not with previous versions
        if (id != null && id != 0) {
            eleve = eleveManager.get(id);
        } else {
            eleve = new Eleve();
        }

        return "detail";
    }
    
    public void printListRetarPay() throws IOException{
    	try {
    		if(retardPayEleves != null){
    			JasperCompileManager.compileReportToFile(getFacesContext().getExternalContext().getRealPath("/reports/retarPay.jrxml"));
    			String sourceFileName = getFacesContext().getExternalContext().getRealPath("/reports/retarPay.jasper");
    			
    			List<RetardPay> retardPays = new ArrayList<>();
    			for (Eleve eleve : retardPayEleves) {
					retardPays.add(new RetardPay(eleve.getFullNameAr(), eleve.getMois()));
				}
    			JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(retardPays);
    			JasperPrint jasperPrint = 
    					JasperFillManager.fillReport(
    							sourceFileName, 
    							new HashMap(),
    							beanCollectionDataSource
    							);
    			
    			JRPdfExporter exporter = new JRPdfExporter();
    			List<JasperPrint> jasperPrints = new ArrayList<JasperPrint>();
    			jasperPrints.add(jasperPrint);
    			exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, jasperPrints);
    			exporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8");
    			exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, getFacesContext().getExternalContext().getRealPath("/reports/listRetardPaiement.pdf"));
    			exporter.setParameter(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS, Boolean.TRUE);
    			
    			exporter.exportReport();
    			
    			Faces.sendFile(new File(getFacesContext().getExternalContext().getRealPath("/reports/listRetardPaiement.pdf")), true);
    		}else
    			addError("errors.eleve");

          } catch (JRException e) {
             e.printStackTrace();
          }
    }
    
    public void printAttestationScolaire() throws IOException{
    	try {
    		id = getParameter("id") != null ? new Long(getParameter("id")) : null;
    		Eleve printEleve = eleveManager.get(id);
    		if(printEleve != null){
    			JasperCompileManager.compileReportToFile(getFacesContext().getExternalContext().getRealPath("/reports/attestationScolaire.jrxml"));
    			String sourceFileName = getFacesContext().getExternalContext().getRealPath("/reports/attestationScolaire.jasper");
    			
    			List<AttestationScolaireBean> attestationScolaireBeans = new ArrayList<>();
    			AttestationScolaireBean attestationScolaireBean = new AttestationScolaireBean(printEleve);
    			attestationScolaireBeans.add(attestationScolaireBean);
    			JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(attestationScolaireBeans);
    			JasperPrint jasperPrint = 
    					JasperFillManager.fillReport(
    							sourceFileName, 
    							new HashMap(),
    							beanCollectionDataSource
    							);
    			
    			JRPdfExporter exporter = new JRPdfExporter();
    			List<JasperPrint> jasperPrints = new ArrayList<JasperPrint>();
    			jasperPrints.add(jasperPrint);
    			exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, jasperPrints);
    			exporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8");
    			exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, getFacesContext().getExternalContext().getRealPath("/reports/attestationScolaire.pdf"));
    			exporter.setParameter(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS, Boolean.TRUE);
    			
    			exporter.exportReport();
    			
    			Faces.sendFile(new File(getFacesContext().getExternalContext().getRealPath("/reports/attestationScolaire.pdf")), true);
    		}else
    			addError("errors.eleve");

          } catch (JRException e) {
             e.printStackTrace();
          }
    }
    
    public void printListeEleve() throws IOException{
    	try {
    		if(elevesResult != null){
    			JasperCompileManager.compileReportToFile(getFacesContext().getExternalContext().getRealPath("/reports/listEleves.jrxml"));
    			String sourceFileName = getFacesContext().getExternalContext().getRealPath("/reports/listEleves.jasper");
    			
    			Map<String, Object> params = new HashMap<>();
    			
    			JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(EleveBean.fillListeEleve(elevesResult));
    			params.put("TableDataSource", beanCollectionDataSource);
    			params.put("AnneeScolaire", appController.getAnneeScolaire().getAnneeScolaire());
    			Groupe groupe = eleve != null && eleve.getGroupe() != null ? groupeManager.get(eleve.getGroupe().getId()) : null;
    			String libelleGroupe = groupe != null ? groupe.getLibelleGroupe() : "";
    			params.put("Groupe", libelleGroupe);
    			
    			JasperPrint jasperPrint = 
    					JasperFillManager.fillReport(
    							sourceFileName, 
    							params,
    							new JREmptyDataSource()
    							);
    			
    			JRPdfExporter exporter = new JRPdfExporter();
    			List<JasperPrint> jasperPrints = new ArrayList<JasperPrint>();
    			jasperPrints.add(jasperPrint);
    			exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, jasperPrints);
    			exporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8");
    			exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, getFacesContext().getExternalContext().getRealPath("/reports/listEleves.pdf"));
    			exporter.setParameter(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS, Boolean.TRUE);
    			
    			exporter.exportReport();
    			
    			Faces.sendFile(new File(getFacesContext().getExternalContext().getRealPath("/reports/listEleves.pdf")), true);
    		}else
    			addError("errors.eleve");

          } catch (JRException e) {
             e.printStackTrace();
          }
    }
    
    public String save() {
        boolean isNew = (eleve.getId() == null || eleve.getId() == 0);
        eleveManager.save(eleve);

        String key = (isNew) ? "eleve.added" : "eleve.updated";
        addMessage(key);

        if (isNew) {
            return "list";
        } else {
            return "edit";
        }
    }
    
    public String importe() {
        if(importResult != null){
        	for (Eleve eleve : importResult) {
				eleveManager.save(eleve);
			}
        	addMessage("import.save.ok");
        }else
        	addMessage("import.save.nok");
        
        
        
        return "input";
    }
    
    public String cancel() {
    	eleve = new Eleve();
        return "cancel";
    }
    
    public void clearForm(){
    	eleve = new Eleve();
    }

    public String getQuery() {
        return query;
    }
    
    public List<Eleve> getEleves() {
        return sort(eleveManager.getAll(), "numeroInscription", true, false);
    }
    
    public List<AnneeScolaire> getAnneeScolaires() {
		List<AnneeScolaire> anneeScolaires = anneeScolaireManager.getAll();
		if (anneeScolaires == null) return new ArrayList<>();
		return sort(anneeScolaires, "anneeScolaire", true, false);
	}
    
	public List<Eleve> getElevesResult() {
		if(elevesResult == null)
				elevesResult = new ArrayList<>();
		return elevesResult;
	}
	
	public List<Eleve> getRetardPayEleves(){
		if(appController == null || appController.getAnneeScolaire() == null){
			addError("errors.anneeScolaire");
			return new ArrayList<>();
		}
		
		if(moisPayeNow == null)
			return new ArrayList<>();
		
		/*
		 * get liste de tt les eleve :
		 * verifier est que parmi ces eleve il y a qq 1 qui a un paiement avec un reste > 0
		 * OU il a aps encore paye pour un mois a partir le mois choisis
		 */
		retardPayEleves = new ArrayList<>();
		List<Eleve> eleves = eleveManager.getAllWithoutExonere();
		List<MoisPaye> moisAPayes = null;
		for (Eleve e : eleves) {
			moisAPayes = new ArrayList<>();
			List<Paiement> paiements = paiementManager.loadPaiementsByEleve(e.getId(), appController.getAnneeScolaire());
			if(paiements != null && !paiements.isEmpty()){
				for (Paiement paiement : paiements) {
					if(paiement.getReste() > 0 && paiement.getMoisPaye().getId() <= moisPayeNow.getId())
						moisAPayes.add(paiement.getMoisPaye());
				}
			}
			
			checkMoisAPaye(moisAPayes, paiements);
			e.setMoisAPayes(sort(moisAPayes, "id", true, false));
			
			if(!moisAPayes.isEmpty())
				retardPayEleves.add(e);
		}	
		
		
		return retardPayEleves != null ? retardPayEleves : new ArrayList<Eleve>();
	}

	private void checkMoisAPaye(List<MoisPaye> moisAPayes,
			List<Paiement> paiements) {
		List<MoisPaye> lisMoisAtNow = moisPayeManager.listMoisAt(moisPayeNow);
		boolean isFound;
		for (MoisPaye moisPaye : lisMoisAtNow) {
			isFound=false;
			for (Paiement paiement : paiements) {
				if(paiement.getMoisPaye().equals(moisPaye)){
					isFound=true;
					break;
				}
			}
			
			if(!isFound)
				moisAPayes.add(moisPaye);
		}
	}
	
	public List<MoisPaye> getMois() {
		List<MoisPaye> mois = moisPayeManager.getAll();
		return mois;
	}

	public Eleve getEleve() {
		if(eleve == null)
			eleve = new Eleve();
		return eleve;
	}

	public void setEleve(Eleve eleve) {
		this.eleve = eleve;
	}
	
	public Classe getClasse() {
		return classe;
	}

	public void setClasse(Classe classe) {
		this.classe = classe;
	}

	public void setId(Long id) {
        this.id = id;
    }
	
	public UploadedFile getFileEleves() {
		return fileEleves;
	}

	public void setFileEleves(UploadedFile fileEleves) {
		this.fileEleves = fileEleves;
	}

	public MoisPaye getMoisPayeNow() {
		if(moisPayeNow == null)
			moisPayeNow = moisPayeManager.getMois(Calendar.getInstance().get(Calendar.MONTH));
		
		return moisPayeNow;
	}

	public void setMoisPayeNow(MoisPaye moisPayeNow) {
		this.moisPayeNow = moisPayeNow;
	}

	public List<Classe> getClasses() {
		classes = classeManager.getAll();
		if (classes == null) return new ArrayList<>();
		return sort(classes, "id", true, false);
	}
	
	public List<Classe> getGroupes() {
		if(groupes == null ){
			groupes = groupeManager.getGroupeByClasse(getClasses().get(1), appController.getAnneeScolaire());
		}
			
		if(groupes != null && !groupes.isEmpty()){
			eleve.setGroupe(groupes.get(0));
			return sort(groupes, "id", true, false);
		}else
			return new ArrayList<>();
	}
	
	 public void handleGroupeChange() {  
		 groupes = groupeManager.getGroupeByClasse(eleve.getGroupe().getClasse(), appController.getAnneeScolaire());
    }
	
	public void handleFileUpload(FileUploadEvent event) throws IOException{
		String uploadDir = "C:/eleves/";

        // Create the directory if it doesn't exist
        File dirPath = new File(uploadDir);

        if (!dirPath.exists()) {
            dirPath.mkdirs();
        }

        //retrieve the file data
        InputStream stream = event.getFile().getInputstream();
        String filename = event.getFile().getFileName();

        // APF-946: Canoo Web Tests R_1702 sets full path as name instead of only file name
        if (filename.contains("/")) {
            filename = filename.substring(filename.lastIndexOf("/") + 1);
        }

        // APF-758: Fix for Internet Explorer's shortcomings
        if (filename.contains("\\")) {
            int slash = filename.lastIndexOf("\\");
            if (slash != -1) {
                filename = filename.substring(slash + 1);
            }
            // Windows doesn't like /'s either
            int slash2 = filename.lastIndexOf("/");
            if (slash2 != -1) {
                filename = filename.substring(slash2 + 1);
            }
            // In case the name is C:foo.txt
            int slash3 = filename.lastIndexOf(":");
            if (slash3 != -1) {
                filename = filename.substring(slash3 + 1);
            }
        }

        //write the file to the file specified
        OutputStream bos = new FileOutputStream(uploadDir + filename);
        int bytesRead;
        byte[] buffer = new byte[8192];

        while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
            bos.write(buffer, 0, bytesRead);
        }

        bos.close();

        //close the stream
        stream.close();

        eleve.setImage(uploadDir + filename);
	}
	
	public void handleFileElevesUpload(FileUploadEvent event) {
		try{
 
            //Create Workbook instance holding reference to .xlsx file
            HSSFWorkbook workbook = new HSSFWorkbook((FileInputStream)event.getFile().getInputstream());
 
            //Get first/desired sheet from the workbook
            HSSFSheet sheet = workbook.getSheet(classeurName);
 
            Eleve eleve;
            importResult = new ArrayList<>();
            for (int rowIndex=1; rowIndex<100;rowIndex++) {
            	HSSFRow row = sheet.getRow(rowIndex);
            	eleve = new Eleve();
            	if(!checkRowEmpty(row)){
	            	//parse Ordre
//            		eleve.setOrdreClasse(Integer.valueOf(getCellValue(row, 0)));
	            	eleve.setNumeroInscription(getCellValue(row, 1));
	            	eleve.setNom(getCellValue(row, 2));
	            	eleve.setPrenom(getCellValue(row, 3));
	            	eleve.setNomAr(getCellValue(row, 4));
	            	eleve.setPrenomAr(getCellValue(row, 5));
	            	eleve.setLieuNaissanceAr(getCellValue(row, 6));
	            	eleve.setLieuNaissance(getCellValue(row, 7));
	            	eleve.setDateNaissance(DateUtil.convertStringToDate(getCellValue(row, 8)));
	            	eleve.setAdresse(getCellValue(row, 9));
	            	eleve.setAdresseAr(getCellValue(row, 10));
	            	eleve.setGroupe(this.eleve.getGroupe());
	            	
	            	importResult.add(eleve);
            	}
        }
            
        }catch (Exception e){
            e.printStackTrace();
        }
    }
	
	private boolean checkRowEmpty(HSSFRow row) {
		for(int i = 0; i < 14; i ++){
			if(row.getCell(i) != null){
				return false;
			}
		}
		
		return true;
	}

	private String getCellValue(HSSFRow row, int i) {
		String cellValue = null;
		
		HSSFCell cell =  row.getCell(i);
		
		if(cell != null){
        	switch (cell.getCellType()){
        	case HSSFCell.CELL_TYPE_NUMERIC:
        		if (HSSFDateUtil.isCellDateFormatted(cell)){
        			Date date = cell.getDateCellValue();
        			SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        			cellValue = sdf.format(date);
        		}else
        			cellValue = String.valueOf(cell.getNumericCellValue());
        		break;
        	case HSSFCell.CELL_TYPE_STRING:
        		cellValue = String.valueOf(cell.getStringCellValue());
        		break;
        	default : 
        		cellValue = String.valueOf(cell.getStringCellValue()) ;
        		break;
        	}
        }
		
		return cellValue;
	}

	public String getClasseurName() {
		return classeurName;
	}

	public void setClasseurName(String classeurName) {
		this.classeurName = classeurName;
	}

	public List<LabelValue> getGenres(){
		List<LabelValue> genres = new ArrayList<>();
		for (GenreEleve genreEleve : GenreEleve.values()) {
			genres.add(new LabelValue(genreEleve.getGenre(), genreEleve));
		}
		
		return genres;
	}
	
	public List<Eleve> getImportResult() {
		return importResult;
	}

	public void setImportResult(List<Eleve> importResult) {
		this.importResult = importResult;
	}

	@Autowired
    public void setEleveManager(@Qualifier("eleveManager") EleveManager eleveManager) {
        this.eleveManager = eleveManager;
    }
	
	@Autowired
    public void setClasseManager(@Qualifier("classeManager") ClasseManager classeManager) {
        this.classeManager = classeManager;
    }
	
	@Autowired
    public void setGroupeManager(@Qualifier("groupeManager") GroupeManager groupeManager) {
        this.groupeManager = groupeManager;
    }
	
	@Autowired
	public void setAnneeScolaireManager(@Qualifier("anneeScolaireManager") AnneeScolaireManager anneeScolaireManager) {
		this.anneeScolaireManager = anneeScolaireManager;
	}

	@Autowired
	public void setPaiementManager(@Qualifier("paiementManager") PaiementManager paiementManager) {
		this.paiementManager = paiementManager;
	}
	
	@Autowired
	public void setMoisPayeManager(@Qualifier("moisPayeManager") MoisPayeManager moisPayeManager) {
		this.moisPayeManager = moisPayeManager;
	}
	
}